#pragma once

#include "Index.hpp"
#include <jsoncpp/json/json.h>

class Search
{
private:
    //根据关键字，从网页正文中截取这个关键字出现位置附近的摘要
    string GetAbstract(const string& content,const string& keyword)
    {
        const ssize_t left_size=100;//截取pos位置前面，left_size个字节
        const ssize_t right_size=100;//截取pos位置后面，right_size个字节

        string ret;
        //找到关键字在网页中首次出现的位置pos

        //ssize_t pos=content.find(keyword);//不能直接用find
        /*if(pos==string::npos)
        {
            //这里为什么可能失败？
            //因为网页文档中的keyword可能是大写的，但是索引里面保存的关键字是小写的
            //而  find  查找是  不忽略  大小写的，所以用小写找大写就找不到了
            LOG(LogLevel::DEBUG)<<"摘要失败,keyword: "<<keyword;
            return "";
        }*/

        //使用search，自己编写比较相等的仿函数
        auto iter=std::search(content.begin(),content.end(),keyword.begin(),keyword.end(),[](int left,int right)
        {
            return std::tolower(left)==std::tolower(right);
        });
        //如果摘要失败
        if(iter==content.end())
        {
            LOG(LogLevel::DEBUG)<<"摘要失败,keyword: "<<keyword;
            return "";
        }

        //获取pos的偏移量
        ssize_t pos=std::distance(content.begin(),iter);

        //截取pos位置前面的left_size个字节
        if(pos>left_size)
        {
            ret+=content.substr(pos-left_size,left_size);
        }
        else//如果之前没有left_size个字节，就从文档最开头开始
        {
            ret+=content.substr(0,pos);
        }

        //截取pos位置后面的right_size个字节
        //如果后面没有right_size个字节了，就截取到文件末尾
        ret+=content.substr(pos,right_size);

        return ret;
    }
public:
    Search()
        : _index(nullptr)
    {
    }
    // parser_file_path是HtmlParser去标签之后的所有网页内容，所存储的文件的路径
    void Init(const string &parser_file_path)
    {
        // 1.获取单例化的Index对象
        _index = Index::GetIndexObj();

        // 2.调用Index对象的成员函数BuildIndex构建索引
        _index->BuildIndex(parser_file_path);
        LOG(LogLevel::DEBUG)<<"构建正排和倒排索引成功";
    }
    // 搜索
    // keyword是用户搜索的内容
    // json_resp是返回给用户的搜索结果，组成的json串
    void SearchIndex(const string &keyword, string *json_resp)
    {
        // 1.把用户的搜索内容进行分词
        vector<string> words;
        means::CppjiebaCatStr(keyword, &words);

        // 2.把分出来的多个词转换成小写之后，使用分出来的多个词，进行倒排查找
        vector<InvertedNode> node_all;
        for (string word : words)
        {
            // 统一转换成小写
            boost::to_lower(word);

            // 使用倒排索引找到与该关键字相关的 所有网页 的网页文档ID
            vector<InvertedNode> *nodes = _index->GetInvertedIndex(word);
            if (nodes == nullptr)//如果没找到
            {
                continue;//就跳过
            }

            //把所有的InvertedNode放进一个数组中，方便排序
            node_all.insert(node_all.end(),nodes->begin(),nodes->end());
        }

        //3.根据相关性进行降序排序
        std::sort(node_all.begin(),node_all.end(),[](const InvertedNode& left,const InvertedNode&right)
        {
            if(left._weight>right._weight)
                return true;
            else
                return false;
        });

        unordered_set<uint64_t> hash;
        //根
        Json::Value root;
        // 4.使用正排查找，找到文档内容
        for (const InvertedNode &node : node_all)
        {
            //去除重复的网页文档
            //因为可能通过不同关键字找到同一份网页文档
            //但是构建响应的话只需要一份网页文档即可
            //而且需要的一定是相关性更高的那一份网页文档
            //所以才需要在相关性降序排序之后，才进行去重
            if(hash.count(node._doc_id)==0)
            {
                hash.insert(node._doc_id);
            }
            else
            {
                //因为是降序排序，所以之前相关性更强的已经被保存了，相关性弱的刚好被去重
                continue;
            }
            // 使用正排索引找到网页文档内容
            DocInfo* info=_index->GetForwardIndex(node._doc_id);
            if(info==nullptr)
            {
                continue;
            }

            // 5.根据找到的文档内容，构建json串
            Json::Value item;//节点
            item["title"]=info->_title;
            item["abstract"]=GetAbstract(info->_content,node._keyword);//获取摘要
            item["url"]=info->_url;

            root.append(item);//节点添加到根
        }

        //序列化成json串
        Json::StyledWriter writer;
        *json_resp=writer.write(root);
    }

private:
    Index *_index; // 用于操作索引模块,实现搜索
};